package com.ch.mybatis;

import com.ch.biz.TruckUser;
import com.ch.mybatis.handler.*;

import java.lang.reflect.*;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/*
*功能：
 作者：chenhao
*日期： 2025/6/4 上午11:04
*/
public class MapperProxyFactory {
    private static Map<Class,TypeHandler> typeHandlerMap = new HashMap<>();
    static {
        typeHandlerMap.put(String.class,new StringTypeHandler());
        typeHandlerMap.put(Integer.class,new IntegerTypeHandler());
        typeHandlerMap.put(Boolean.class,new BooleanTypeHandler());
        typeHandlerMap.put(Timestamp.class,new TimeStampTypeHandler());
        typeHandlerMap.put(Long.class,new LongTypeHandler());

        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
    public static <T> T getMapper(Class<T> mapper){
        Object proxyInstance = Proxy.newProxyInstance(mapper.getClassLoader(), new Class[]{mapper}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //1.创建数据库连接
                Connection connection = getConnection();
                //2.构造PreparedStatement

                Select annotation = method.getAnnotation(Select.class);
                String sql = annotation.value();
                ParamterMappingTokenHandler tokenHandler  = new ParamterMappingTokenHandler();
                GenericTokenParser parser = new GenericTokenParser("#{", "}",tokenHandler);
                String newSql = parser.parse(sql);
                PreparedStatement preparedStatement = connection.prepareStatement(newSql);

                /*
                构造#{}中参数和传入参数的关系
                如 #{phone}  -> 13000000000
                *
                *
                * */
                List<ParamterMapping> parameterMappings = tokenHandler.getParameterMappings();


               /*
                这个也一样，但
               跟上面不同的是，这个包括了原始的参数顺序名
                （包含arg0，arg1。。。。。。。。。）
                */
                Map<Object,Object> parameterMap = new HashMap<>();
                // 获取参数个数
                Parameter[] parameters = method.getParameters();
                for (int i = 0; i < parameters.length; i++) {
                    Parameter parameter = parameters[i];
                    String name = parameter.getAnnotation(Param.class).value();
                    parameterMap.put(parameter.getName(),args[i]);
                    parameterMap.put(name,args[i]);
                }

                for (int i = 0; i < parameterMappings.size(); i++) {
                    String property = parameterMappings.get(i).getProperty();
                    Object value = parameterMap.get(property);
                    Class<?> type = value.getClass();
                    System.out.println("value=" + value + ", type=" + type.getName());
                    //使用static里提前注册的适配器对?中传入的不同的参数类型进行处理
                    typeHandlerMap.get(type).setParameter(preparedStatement, i + 1, value);
                }

                //3.执行PreparedStatement
                preparedStatement.execute();


                //4.封装结果
                ResultSet resultSet = preparedStatement.getResultSet();
                //根据方法返回类型确定最终返回数据
                List<Object> list = new ArrayList<>();

                ResultSetMetaData metaData = resultSet.getMetaData();
                int columnCount = metaData.getColumnCount();

                //每次while代表一行数据
                Class<?> returnType = method.getReturnType(); // 通常是 List
                Type genericReturnType = method.getGenericReturnType();
                if(genericReturnType instanceof Class){
                    //不是泛型
                    returnType = (Class) genericReturnType;
                }else if (genericReturnType instanceof ParameterizedType){
                    //泛型
                    Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
                    returnType = (Class) actualTypeArguments[0];
                }
                while (resultSet.next()) {


                    Object obj = returnType.getDeclaredConstructor().newInstance();
                    //每次for循环代表一个字段
                    for (int i = 1; i <= columnCount; i++) {
                        String columnName = metaData.getColumnLabel(i);
                        Object value = resultSet.getObject(i);

                        Field field = returnType.getDeclaredField(columnName);
                        field.setAccessible(true);
                        field.set(obj, value);
                    }
                    list.add(obj);
                }
                //5.关闭数据库连接
                connection.close();
                return list;
            }
        });
        return (T) proxyInstance;
    }

    private static Connection getConnection() throws SQLException {
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/truck",  "root", "123456");
        return connection;
    }
}
